package com.hr;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.espirit.eap.manager.Department;
import com.espirit.eap.manager.DepartmentService;
import com.espirit.eap.sa.Employee;
import com.espirit.finance.core.Acct;
import com.espirit.finance.core.AcctItem;
import com.espirit.finance.core.AcctItemService;
import com.espirit.finance.core.DefaultAcctItemCompare;
import com.googlecode.cswish.annotation.GenericType;
import com.googlecode.cswish.annotation.ParamScope;
import com.googlecode.cswish.annotation.Rest;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.model.PageElements;
import com.googlecode.cswish.model.PageInfo;
import com.googlecode.cswish.model.Reference;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.util.bean.ModelUtil;

@Service
public class SalaryAcctItemService extends AcctItemService{
	@Resource
	DepartmentService departmentService;
	
	private Map<Integer, Set<EmployeeAcctItemInfo>> acctItemEmps;
	private Map<Integer, Set<SalaryAcctItem>> employeeAcctItems;
	
	protected void formatData(PageInfo pageInfo, PageElements ps){
		if (pageInfo == null) return;
		for(Object obj : pageInfo.getData()){
			SalaryAcctItem sai = (SalaryAcctItem) obj;
			formatAcctItemInfo(sai);
		}
	}
	
	void formatAcctItemInfo(SalaryAcctItem sai){
//		StringBuilder sb = new StringBuilder();
//		String deptIds = sai.getDeptIds();
//		if (StringUtils.isEmpty(ids)) return;
//		String[] deptIds = ids.split(",");
//		for(String id : deptIds){
//			Department dept = genericService.load(Department.class, Integer.valueOf(id));
//			if (dept != null){
//				sb.append(dept.getName()).append(",");
//			}
//		}
//		String deptNames = sb.toString();
//		if (!StringUtils.isEmpty(deptNames)){
//			deptNames = deptNames.substring(0, deptNames.length() - 1);
//		}
//		sai.setDeptNames(deptNames);
		
		sai.setDeptNames(getNames(Department.class, sai.getDeptIds()));
//		sai.setWorkGroupNames(getNames(WorkGroup.class, sai.getWorkGroupIds()));
		
	}
	
	private String getNames(Class clazz, String ids){
		StringBuilder sb = new StringBuilder();
		
		if (StringUtils.isEmpty(ids)) return null;
		String[] deptIds = ids.split(",");
		for(String id : deptIds){
			Object dept = genericService.load(clazz, Integer.valueOf(id));
			if (dept != null){
				sb.append(ModelUtil.getProperty(dept, "name")).append(",");
			}
		}
		String names = sb.toString();
		if (!StringUtils.isEmpty(names)){
			names = names.substring(0, names.length() - 1);
		}
		return names;
	}
	
	public List<SalaryAcctItem> getValidSalaryAcctItems(){
		SalaryAcctItem example = new SalaryAcctItem();
		return super.getValidAcctItems(example);
	}
	
	public void add(SalaryAcctItem model, Reference<Integer> parentId){
		model = (SalaryAcctItem) super.add(model, parentId);;
		onChanged(model, true);
	}
	
	@Transactional
	public void update(SalaryAcctItem model){
		model = (SalaryAcctItem) genericService.update(model);
		onChanged(model, true);
	}
	
	@Transactional
	public void deleteList(@GenericType List<Object> results){
		boolean flag = genericService.deleteList(results);
		if (flag){
			for(Object obj : results){
				SalaryAcctItem acctItem = (SalaryAcctItem) obj;
				acctItem = genericService.load(acctItem);
				onChanged(acctItem);
			}
		}
	}
	
	public Map<Integer, Set<EmployeeAcctItemInfo>> getAcctItemEmps(){
		if (acctItemEmps == null || acctItemEmps.isEmpty()){
			onChanged();
		}
		return acctItemEmps;
	}
	
	@Async
	public synchronized void onChanged(){
		SalaryAcctItem example = new SalaryAcctItem();
		List<SalaryAcctItem> results = genericService.searchAll(example).getData();
		if (results != null){
			for(SalaryAcctItem salaryAcctItem : results){
				onChanged(salaryAcctItem);
			}
		}
	}
	
	private void onChanged(SalaryAcctItem salaryAcctItem){
		onChanged(salaryAcctItem, false);
	}
	
	private synchronized void onChanged(SalaryAcctItem salaryAcctItem, boolean check){
		if (salaryAcctItem == null || salaryAcctItem.getId() == null) return ;
		Integer key = salaryAcctItem.getId();
		if (acctItemEmps == null){
			acctItemEmps = new HashMap<Integer, Set<EmployeeAcctItemInfo>>();
		}
		
		acctItemEmps.remove(key);
		if (!(salaryAcctItem.getActive().equals(AcctItem.ACTIVE_YES)) || !(salaryAcctItem.getParent().getActive() .equals(Acct.ACTIVE_YES))){
			return ;
		} 
		
		String deptIds = salaryAcctItem.getDeptIds();
		if (StringUtils.isEmpty(deptIds)) {
			Department root = departmentService.getRootDepartment();
			if (root == null){
				return ;
			}
			deptIds = root.getId().toString();
		}
		
		Set<EmployeeAcctItemInfo> setEmployees = acctItemEmps.get(key);
		if (setEmployees == null){
			setEmployees = new HashSet<EmployeeAcctItemInfo>();
			acctItemEmps.put(key, setEmployees);
		} 
		Integer parentAcctId = salaryAcctItem.getParent().getId();
		List<AcctItem> parentAcctItems = null;
		if (check){
			parentAcctItems = salaryAcctItem.getParent().getChildren();
		}
		
		Integer attribute = salaryAcctItem.getEmployeeAttribute();
		
		String[] ids = deptIds.split(",");
		for(String deptId : ids){
			Set<Employee> employees = departmentService.getValidDescEmployees(Integer.valueOf(deptId));
			if (employees != null){
				for(Employee emp : employees){
					if (attribute != null){
						if (!attribute.equals(emp.getAttribute())){
							continue;
						}
					}
					EmployeeAcctItemInfo eaii = new EmployeeAcctItemInfo(parentAcctId, salaryAcctItem.getId(), emp.getId(), Integer.valueOf(deptId));
					if (check && parentAcctItems != null){
						for(AcctItem acctItem : parentAcctItems){
							if (!acctItem.getId().equals(salaryAcctItem.getId())){
								Set<EmployeeAcctItemInfo> otherEmps = acctItemEmps.get(acctItem.getId());
								if (otherEmps != null){
									for(EmployeeAcctItemInfo other : otherEmps){
										if (other.getParentId().equals(eaii.getParentId()) && other.getSrcDeptId().equals(eaii.getSrcDeptId()) ){
											Department dept = genericService.load(Department.class, other.getSrcDeptId());
											String msg = "应用范围重复：" + salaryAcctItem.getName() + "：" + acctItem.getName() + " 部门："+ dept.getName();
											throw new ApplicationException(msg);
										}
									}
								}
							}
						}
					}
					setEmployees.add(eaii);
				}
			}
		}
	}
	
	@Rest(simpleServiceName="loginUser", type="free")
	public  List<SalaryAcctItem> searchRelatedResult(final Employee model, int start, int limit,
    		String sort, String dir, boolean newSearch, Reference<QLInfo> qlInfoRef,
    		@ParamScope(visible=false, value="#ps")PageElements ps ,boolean fromMenu ) {
		Integer empId = model.getId();
		return getSalaryAcctItems(empId);
	}
	
	public List<SalaryAcctItem> getSalaryAcctItems(Integer empId) {
		Map<Integer, Set<EmployeeAcctItemInfo>> acctItemEmps = getAcctItemEmps();
		if (empId == null || acctItemEmps == null) return null;
		List<SalaryAcctItem> results = new ArrayList<SalaryAcctItem>();
		
		Map<String, EmployeeAcctItemInfo> acctEmployees = new HashMap<String, EmployeeAcctItemInfo>();
		
		//make it only
		for(Set<EmployeeAcctItemInfo> eaiis : acctItemEmps.values()){
			for(EmployeeAcctItemInfo eaii : eaiis){
				if (eaii.getDestEmpId().equals(empId)){
					String key = eaii.getKey();
					EmployeeAcctItemInfo in = acctEmployees.get(key);
					if (in == null){
						acctEmployees.put(key, eaii);
					} else {
						if (eaii.isHighPrior(in)){
							acctEmployees.put(key, eaii);
						}
					}
				}
			}
		}
		//TODO: need it cache?
		for(EmployeeAcctItemInfo si : acctEmployees.values()){
			if (si.getDestEmpId().equals(empId)){
				SalaryAcctItem salaryAcctItem = genericService.load(SalaryAcctItem.class, si.getAcctItemId());
				formatAcctItemInfo(salaryAcctItem);
				results.add(salaryAcctItem);
			}
		}
		
		Collections.sort(results, new DefaultAcctItemCompare());
		
		return results;
	}
	
	class EmployeeAcctItemInfo{
		Integer destEmpId;
		Integer srcDeptId;
		Integer parentId;
		Integer acctItemId;
		String key;
		
		public EmployeeAcctItemInfo(Integer parentAcctId, Integer acctItemId, Integer destEmpId, Integer srcDeptId) {
			super();
			this.destEmpId = destEmpId;
			this.srcDeptId = srcDeptId;
			this.parentId = parentAcctId;
			this.acctItemId = acctItemId;
			this.key = parentAcctId.toString() + "#" + destEmpId.toString();
		}
		
		public Integer getDestEmpId() {
			return destEmpId;
		}

		public Integer getSrcDeptId() {
			return srcDeptId;
		}

		public Integer getParentId() {
			return parentId;
		}
		
		public Integer getAcctItemId(){
			return acctItemId;
		}
		
		public String getKey(){
			return key;
		}
		
		public boolean isHighPrior(EmployeeAcctItemInfo other){
			return departmentService.isChild(this.srcDeptId, other.srcDeptId);
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result ;
			result = prime * result + ((destEmpId == null) ? 0 : destEmpId.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			EmployeeAcctItemInfo other = (EmployeeAcctItemInfo) obj;
			if (destEmpId == null) {
				if (other.destEmpId != null)
					return false;
			} else if (!destEmpId.equals(other.destEmpId))
				return false;
			return true;
		}
	}
}
